#!/usr/bin/env node
/* eslint-disable no-console */

const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const creatorPkg = require('./package.json');

const args = process.argv.slice(2);
let extensionName = '';
let appName = '';
let updateOnly = false;
let skeletonOnly = false;
let ignoreShellDepCheck = false;
let tagUsed = ''; // To store the inferred tag

args.forEach((arg, index) => {
  switch (arg) {
  case '--update':
  case '-u':
    updateOnly = true;
    break;
  case '--app-name':
  case '-a':
    if ( args[index + 1] && !args[index + 1].startsWith('-') ) {
      appName = args[index + 1];
    } else {
      console.error('Error: Missing value for --app-name or -a option.');
      process.exit(1);
    }
    break;
  case '--skeleton-only':
  case '-s':
    skeletonOnly = true;
    break;
  case '-i':
    ignoreShellDepCheck = true;
    break;
  default:
    if ( !arg.startsWith('-') && extensionName === '' ) {
      extensionName = arg;
      appName = appName || extensionName;
    }
  }
});

if ( !extensionName && !updateOnly && !skeletonOnly ) {
  console.error('Please provide an extension name.');
  process.exit(1);
}

// Infer the tag used based on the installed version and dist-tags
try {
  const packageName = creatorPkg.name;
  const currentVersion = creatorPkg.version;

  // Fetch the dist-tags from npm
  const distTags = JSON.parse(execSync(`npm view ${ packageName } dist-tags --json`).toString());

  // Find the tag matching the current version
  tagUsed = Object.keys(distTags).find((tag) => distTags[tag] === currentVersion) || 'latest';

  console.log(`Inferred tag used: ${ tagUsed }`);
} catch (error) {
  console.error('Error inferring tag:', error.message);
  process.exit(1);
}

// Now, check the version of `@rancher/shell` package based on the inferred tag
let shellVersion = 'latest';
const shellPackageName = '@rancher/shell';

try {
  // Fetch the version of the `@rancher/shell` package that corresponds to the inferred tag
  const tagVersion = execSync(`npm view ${ shellPackageName } dist-tags.${ tagUsed }`).toString().trim();

  if (tagVersion) {
    shellVersion = tagVersion;
  } else {
    const latestVersion = execSync(`npm view ${ shellPackageName } version`).toString().trim();

    shellVersion = latestVersion;
  }
} catch (error) {
  console.error(`  Failed to determine version for ${ shellPackageName }:`, error.message);
  process.exit(1);
}

console.log(`  Using version ${ shellVersion } for ${ shellPackageName }`);

const basePath = process.cwd();
let skeletonPath;
let isInsideSkeleton = false;
let directoryExists = false;

// Check if we are inside a skeleton application by looking for package.json
if ( fs.existsSync(path.join(basePath, 'package.json')) ) {
  // Check for @rancher/shell dependency
  const packageJsonPath = path.join(basePath, 'package.json');
  const packageJson = require(packageJsonPath);

  if ( !ignoreShellDepCheck && (!packageJson.dependencies || !packageJson.dependencies['@rancher/shell']) ) {
    throw new Error('@rancher/shell dependency is missing in package.json.');
  } else {
    isInsideSkeleton = true;
    skeletonPath = basePath;
  }
} else {
  // If not inside a skeleton, check if a directory with the appName already exists
  skeletonPath = path.join(basePath, appName);

  if ( fs.existsSync(skeletonPath) ) {
    directoryExists = true;
  }
}

const pkgPath = path.join(skeletonPath, 'pkg', extensionName);
const updatePath = path.join(__dirname, 'update');

const skeletonExists = fs.existsSync(skeletonPath);
const pkgExists = fs.existsSync(pkgPath);

try {
  if ( updateOnly ) {
    // Run the update script directly
    console.log('Updating applications...');
    execSync(`node ${ path.join(updatePath, 'init') }`, { stdio: 'inherit' });

    console.log('Update completed successfully.');
    process.exit(0);
  }

  // If the directory exists but we're not inside a skeleton, we should exit to prevent overwriting
  if ( directoryExists && !isInsideSkeleton ) {
    throw new Error(`A directory named "${ appName }" already exists. Aborting.`);
  }

  // Create skeleton application if it doesn't exist
  if ( !isInsideSkeleton && !skeletonExists ) {
    console.log(`Creating skeleton application: ${ appName }...`);
    // Pass all arguments to the app/init script
    execSync(`node ${ path.join(__dirname, 'app', 'init') } ${ appName }  ${ shellVersion } ${ args.join(' ') }`, { stdio: 'inherit' });

    // Ensure the skeleton path directory is created before attempting to change directory
    if ( !fs.existsSync(skeletonPath) ) {
      throw new Error(`Failed to create skeleton application directory: ${ skeletonPath }`);
    }

    // Change working directory to the newly created skeleton app
    process.chdir(skeletonPath);
  } else if ( isInsideSkeleton ) {
    // If skeleton exists, ensure the working directory is set correctly
    process.chdir(skeletonPath);
  }

  if ( skeletonOnly ) {
    console.log('Skeleton application created successfully. No additional packages will be installed.');
    process.exit(0);
  }

  if ( pkgExists ) {
    throw new Error(`A package directory for "${ extensionName }" already exists.`);
  }

  // Check for package existence and create it if necessary
  if ( !pkgExists ) {
    console.log(`Creating package: ${ extensionName }...`);
    execSync(`node ${ path.join(__dirname, 'pkg', 'init') } ${ extensionName } ${ shellVersion } ${ args.join(' ') }`, { stdio: 'inherit' });
  }

  if ( args.includes('--update') || args.includes('-u') ) {
    // Run the update script
    console.log('Updating applications...');
    execSync(`node ${ path.join(updatePath, 'init') } ${ extensionName }`, { stdio: 'inherit' });
  }

  console.log('Extension created successfully.');

  if ( skeletonOnly || !isInsideSkeleton ) {
    console.log(`To begin, run: \n\n\tcd ${ appName } && yarn install\n`);
  }
} catch (error) {
  console.error('Error creating extension:', error);
  process.exit(1);
}

/* eslint-enable no-console */
